﻿using AutoMapper;
using FrameworkCore.Attributes;
using FrameworkCore.Constants;
using FrameworkCore.Extensions;
using FrameworkCore.Modules.Startups;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;

namespace FrameworkCore.Modules.Engines;

#nullable disable

/// <summary>
/// 引擎
/// </summary>
public partial class NetModulerEngine : IEngine
{
    #region 实用工具

    /// <summary>
    /// 获取 IServiceProvider
    /// </summary>
    /// <returns>IServiceProvider</returns>
    protected virtual IServiceProvider GetServiceProvider(IServiceScope scope = null)
    {
        if (scope == null)
        {
            var accessor = ServiceProvider?.GetService<IHttpContextAccessor>();
            var context = accessor?.HttpContext;
            return context?.RequestServices ?? ServiceProvider;
        }
        return scope.ServiceProvider;
    }

    /// <summary>
    ///运行启动任务
    /// </summary>
    protected virtual void RunStartupTasks()
    {
        // 查找其他程序集提供的启动任务
        var typeFinder = Singleton<ITypeFinder>.Instance;
        var startupTasks = typeFinder.FindClassesOfType<IStartupTask>();

        // 创建并排序启动任务的实例
        // 即使对于未安装的插件，我们也会启动此接口。
        // 否则，DbContext 初始化程序不会运行，插件安装将无法工作。
        var instances = startupTasks
            .Select(startupTask => (IStartupTask)Activator.CreateInstance(startupTask))
            .Where(startupTask => startupTask != null)
            .OrderBy(startupTask => startupTask.Order);

        // 执行任务
        foreach (var task in instances)
            task.Execute();
    }

    /// <summary>
    /// 注册和配置 AutoMapper
    /// </summary>
    protected virtual void AddAutoMapper(IServiceCollection services, Action<IMapperConfigurationExpression> configure = null)
    {
        List<(Type from, Type[] targets)> maps = new List<(Type from, Type[] targets)>();
        var path = AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory;
        var assemblyFiles = Directory.GetFiles(path, GlobalConstants.AssemblySuffix);

        var modulePath = ApplicationPartManagerExtensions.GetModulesDirectory();
        if (!string.IsNullOrWhiteSpace(modulePath))
        {
            var moduleAssemblyFiles = Directory.GetFiles(modulePath, GlobalConstants.AssemblySuffix);
            assemblyFiles = assemblyFiles.Concat(moduleAssemblyFiles).ToArray();
            assemblyFiles = assemblyFiles.Distinct().ToArray();
        }

        var filteredAssemblyFiles = assemblyFiles.Where(file => !file.Contains("System.") && !file.Contains("Microsoft."));
        var referencedAssemblies = filteredAssemblyFiles.Select(Assembly.LoadFrom).ToList();

        Type[] allTypes = referencedAssemblies.SelectMany(x => x.GetTypes()).ToArray();
        maps.AddRange(allTypes.Where(x => x.GetCustomAttribute<MapAttribute>() != null)
            .Select(x => (x, x.GetCustomAttribute<MapAttribute>()!.TargetTypes)));

        var configuration = new MapperConfiguration(cfg =>
        {
            maps.ForEach(aMap =>
            {
                aMap.targets.ToList().ForEach(aTarget =>
                {
                    cfg.CreateMap(aMap.from, aTarget).IgnoreAllNonExisting(aMap.from, aTarget).ReverseMap();
                });
            });
            cfg.AddMaps(referencedAssemblies);

            //自定义映射
            configure?.Invoke(cfg);
        });

#if DEBUG
        //只在Debug时检查配置
        configuration.AssertConfigurationIsValid();
#endif
        services.AddSingleton(configuration.CreateMapper());
    }

    protected virtual Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
        var assemblyFullName = args.Name;

        // 检查是否已加载程序集
        var assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == assemblyFullName);
        if (assembly != null)
            return assembly;

        // 从 TypeFinder 获取程序集
        var typeFinder = Singleton<ITypeFinder>.Instance;

        return typeFinder?.GetAssemblyByName(assemblyFullName);
    }

    #endregion

    #region 方法

    /// <summary>
    /// 添加和配置服务
    /// </summary>
    /// <param name="services">服务描述符的集合</param>
    /// <param name="configuration">应用程序的配置</param>
    public virtual void ConfigureServices(IServiceCollection services, IConfiguration configuration)
    {
        // 注册引擎
        services.AddSingleton<IEngine>(this);

        // 查找其他程序集提供的启动配置
        var typeFinder = Singleton<ITypeFinder>.Instance;
        var startupConfigurations = typeFinder.FindClassesOfType<INetModulerStartup>();

        // 创建并排序启动配置的实例
        var instances = startupConfigurations
            .Select(startup => (INetModulerStartup)Activator.CreateInstance(startup))
            .Where(startup => startup != null)
            .OrderBy(startup => startup.Order);

        // 配置服务
        foreach (var instance in instances)
            instance.ConfigureServices(services, configuration);

        services.AddSingleton(services);

        // 注册映射配置
        AddAutoMapper(services);

        // 运行启动任务
        RunStartupTasks();

        // 在这里解析程序集。否则，插件在渲染视图时可能会抛出异常
        AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
    }

    /// <summary>
    /// 配置 HTTP 请求管道
    /// </summary>
    /// <param name="application">用于配置应用程序请求管道的构建器</param>
    public virtual void ConfigureRequestPipeline(IApplicationBuilder application)
    {
        ServiceProvider = application.ApplicationServices;

        // 查找其他程序集提供的启动配置
        var typeFinder = Singleton<ITypeFinder>.Instance;
        var startupConfigurations = typeFinder.FindClassesOfType<INetModulerStartup>();

        // 创建并排序启动配置的实例
        var instances = startupConfigurations
            .Select(startup => (INetModulerStartup)Activator.CreateInstance(startup))
            .Where(startup => startup != null)
            .OrderBy(startup => startup.Order);

        // 配置请求管道
        foreach (var instance in instances)
            instance.Configure(application);
    }

    /// <summary>
    /// 解析依赖
    /// </summary>
    /// <typeparam name="T">要解析的服务类型</typeparam>
    /// <param name="scope">作用域</param>
    /// <returns>解析得到的服务实例</returns>
    public virtual T Resolve<T>(IServiceScope scope = null) where T : class
    {
        return (T)Resolve(typeof(T), scope);
    }

    /// <summary>
    /// 解析依赖
    /// </summary>
    /// <param name="type">要解析的服务类型</param>
    /// <param name="scope">作用域</param>
    /// <returns>解析得到的服务实例</returns>
    public virtual object Resolve(Type type, IServiceScope scope = null)
    {
        return GetServiceProvider(scope)?.GetService(type);
    }

    /// <summary>
    /// 解析多个依赖
    /// </summary>
    /// <typeparam name="T">要解析的服务类型</typeparam>
    /// <returns>解析得到的服务集合</returns>
    public virtual IEnumerable<T> ResolveAll<T>()
    {
        return GetServiceProvider()?.GetServices<T>();
    }

    /// <summary>
    /// 解析未注册的服务
    /// </summary>
    /// <param name="type">服务的类型</param>
    /// <returns>解析得到的服务实例</returns>
    public virtual object ResolveUnregistered(Type type)
    {
        Exception innerException = null;
        foreach (var constructor in type.GetConstructors())
        {
            try
            {
                // 尝试解析构造函数的参数
                var parameters = constructor.GetParameters().Select(parameter =>
                {
                    var service = Resolve(parameter.ParameterType) ?? throw new Exception("未知的依赖项");
                    return service;
                });

                // 如果一切顺利，创建实例
                return Activator.CreateInstance(type, parameters.ToArray());
            }
            catch (Exception ex)
            {
                innerException = ex;
            }
        }

        throw new Exception("找不到满足所有依赖关系的构造函数。", innerException);
    }
    #endregion

    #region 属性

    /// <summary>
    /// 服务提供者
    /// </summary>
    public virtual IServiceProvider ServiceProvider { get; protected set; }

    #endregion
}
